package com.ruoyi.common.utils;

import java.util.concurrent.atomic.AtomicLong;

/**
 * 雪花算法 ID 生成器（64位ID）
 * 结构：0（符号位） + 41位时间戳 + 10位机器ID + 12位序列号
 */
public  class  IDUtils {

    // ============================== 常量 ==============================
    /** 开始时间戳（UTC 2020-01-01 00:00:00） */
    private static final long START_TIMESTAMP = 1577808000000L;

    /** 机器ID所占的位数 */
    private static final  long WORKER_ID_BITS = 10L;

    /** 序列号所占的位数 */
    private static final long SEQUENCE_BITS = 12L;

    /** 机器ID的最大值（2^10 - 1 = 1023） */
    private static final long MAX_WORKER_ID = ~(-1L << WORKER_ID_BITS);

    /** 序列号的最大值（2^12 - 1 = 4095） */
    private static final long MAX_SEQUENCE = ~(-1L << SEQUENCE_BITS);

    /** 机器ID左移位数（12位） */
    private static final long WORKER_ID_SHIFT = SEQUENCE_BITS;

    /** 时间戳左移位数（12 + 10 = 22位） */
    private static final long TIMESTAMP_SHIFT = SEQUENCE_BITS + WORKER_ID_BITS;

    // ============================== 变量 ==============================
    /** 机器ID（0~1023） */

    /** 序列号（0~4095） */
    private final AtomicLong sequence = new AtomicLong(0);

    /** 上一次生成ID的时间戳 */
    private volatile long lastTimestamp = -1L;

    // ============================== 构造函数 ==============================
    /**
     * 初始化生成器
     * @param workerId 机器ID（0~1023），分布式环境下需保证每个机器唯一
     */


    // ============================== 方法 ==============================
    /**
     * 生成下一个唯一ID
     *
     * @return 雪花算法ID（64位长整型）
     */
    public synchronized long nextId() {
        long timestamp = System.currentTimeMillis();

        // 1. 校验时间戳（防止时钟回拨）
        if (timestamp < lastTimestamp) {
            throw new RuntimeException(
                String.format("时钟回拨异常！当前时间戳 %d 小于上次时间戳 %d", timestamp, lastTimestamp)
            );
        }

        // 2. 同一毫秒内，序列号自增
        if (timestamp == lastTimestamp) {
            // 序列号自增（& MAX_SEQUENCE 确保不超过最大值）
            long nextSequence = sequence.incrementAndGet() & MAX_SEQUENCE;
            // 若序列号序列号用尽，等待下一毫秒
            if (nextSequence == 0) {
                timestamp = waitNextMillis(lastTimestamp);
            }
            sequence.set(nextSequence);
        } else {
            // 新的毫秒，序列号重置为0
            sequence.set(0);
        }

        // 3. 更新上一次时间戳

        // 4. 组合ID：时间戳位移 + 机器ID位移 + 序列号
        return (timestamp - START_TIMESTAMP) << TIMESTAMP_SHIFT  // 时间戳部分
                | sequence.get();                                // 序列号部分
    }

    /**
     * 等待到下一毫秒（解决序列号用尽的情况）
     * @param lastTimestamp 上一次的时间戳
     * @return 新的时间戳
     */
    private long waitNextMillis(long lastTimestamp) {
        long timestamp = System.currentTimeMillis();
        while (timestamp <= lastTimestamp) {
            timestamp = System.currentTimeMillis();
        }
        return timestamp;
    }
}